﻿using System;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;

namespace Epicor.Pub
{
    /// <summary>本类用来将字符串转为可执行文本并执行</summary>   
    public class PubEvaluator
    {
        /// <summary> 用于动态引用生成的类，执行其内部包含的可执行字符串</summary>   
        object _Compiled = null;
        /// <summary>可执行串的构造函数</summary>   
        public PubEvaluator(EvaluatorItem[] items)
        {
            ConstructEvaluator(items);      //调用解析字符串构造函数进行解析   
        } 
        /// <summary>可执行串的构造函数</summary>   
        public PubEvaluator(EvaluatorItem item)
        {
            EvaluatorItem[] items = new EvaluatorItem[] { item };
            ConstructEvaluator(items);     
        }
        /// <summary>解析字符串构造函数</summary>   
        /// <param name="items">待解析字符串数组</param>   
        private void ConstructEvaluator(EvaluatorItem[] items)
        {
            CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"); //创建C#编译器实例
            CompilerParameters cp = new CompilerParameters();//编译器的传入参数   
            cp.ReferencedAssemblies.Add("system.dll");              //添加程序集 system.dll 的引用   
            cp.ReferencedAssemblies.Add("system.data.dll");         //添加程序集 system.data.dll 的引用   
            cp.ReferencedAssemblies.Add("system.xml.dll");          //添加程序集 system.xml.dll 的引用   
            cp.GenerateExecutable = false;                          //不生成可执行文件   
            cp.GenerateInMemory = true;                             //在内存中运行   
            StringBuilder code = new StringBuilder();               //创建代码串   
            code.Append("using System; "); //  添加常见且必须的引用字符串  
            code.Append("using System.Data; ");
            code.Append("using System.Data.SqlClient; ");
            code.Append("using System.Data.OleDb; ");
            code.Append("using System.Xml; ");
            code.Append("namespace Epicor.Pub { ");                  //生成代码的命名空间   
            code.Append("public class _Evaluator { ");          //产生 _Evaluator 类，所有可执行代码均在此类中运行   
            foreach (EvaluatorItem item in items)               //遍历每一个可执行字符串项   
            {
                //添加定义公共函数代码    
                string fun = "public " + item.ReturnType.Name + " " + item.Name + "() { return (" + item.Expression + ");}";
                code.Append(fun); 
            }
            code.Append("} }"); //添加类结束和命名空间结束括号   
            CompilerResults cr = provider.CompileAssemblyFromSource(cp, code.ToString()); //得到编译器实例的返回结果   
            if (cr.Errors.HasErrors)                            //如果有错误   
            {
                StringBuilder error = new StringBuilder();          //创建错误信息字符串   
                error.Append("编译有错误的表达式: ");                //添加错误文本   
                foreach (CompilerError err in cr.Errors)            //遍历每一个出现的编译错误   
                {
                    error.AppendFormat("{0}", err.ErrorText);     //添加进错误文本，每个错误后换行   
                }
                throw new Exception("编译错误: " + error.ToString());//抛出异常   
            }
            Assembly a = cr.CompiledAssembly;                       //获取编译器实例的程序集   
            _Compiled = a.CreateInstance("Epicor.Pub._Evaluator");     //通过程序集查找并声明 Epicor.Pub._Evaluator 的实例   
        }
        /// <summary> 执行字符串并返 object 型值</summary>   
        /// <param name="name">执行字符串名称</param>   
        public object Evaluate(string name)
        {
            MethodInfo mi = _Compiled.GetType().GetMethod(name);//获取 _Compiled 所属类型中名称为 name 的方法的引用   
            return mi.Invoke(_Compiled, null);                //执行 mi 所引用的方法   
        }
    }

    /// <summary>可执行字符串项（即一条可执行字符串）</summary>   
    public class EvaluatorItem
    {
        /// <summary>返回值类型</summary>   
        public Type ReturnType;
        /// <summary>执行表达式</summary>   
        public string Expression;
        /// <summary>执行字符串名称 </summary>   
        public string Name;
        /// <summary>可执行字符串项构造函数</summary>   
        public EvaluatorItem(Type returnType, string expression, string name)
        {
            ReturnType = returnType;
            Expression = expression;
            Name = name;
        }
    }
}
